#include "arptool.h"

#include <QAction>
#include <QString>
#include <QIcon>
#include <QMessageBox>

#include "public.h"

#include "pcap.h"

#include "types.h"
#include "ethernet.h"
#include "ether_arp.h"
#include "etherdevice.h"
#include "toolkit.h"

ArpTool::ArpTool(QWidget *parent, Qt::WFlags flags)
	: QMainWindow(parent, flags)
{
	ui.setupUi(this);
}

ArpTool::~ArpTool()
{

}

bool ArpTool::init()
{
	
	
	ToolKit toolkit;
	sstring ret = toolkit.getAllEtherDevice(this->devices);
	if (!ret.empty())
	{
		QMessageBox::critical(this, "tips", ret.c_str());
		return false;
	}

	this->ip_range_box = new QComboBox(this);
	for (int i = 0; i < this->devices.size(); i++)
	{
		EtherDevice dev = this->devices[i];
		if (dev.ip != 0 && dev.netmask != 0)
		{
			u_int32 host_order_netnask = ntohl(dev.netmask);
			u_int32 local_host_count = host_order_netnask ^ 0xffffffff;
			u_int32 host_order_local_ip = ntohl(dev.ip);
			u_int32 host_order_start_ip = (host_order_netnask & host_order_local_ip) + 1;
			u_int32 host_order_end_ip = host_order_start_ip + local_host_count - 1;
			
			u_int32 start_ip_n = htonl(host_order_start_ip);
			u_int32 end_ip_n = htonl(host_order_end_ip);
			QString str = QString("%1 - %2")
				.arg(inet_ntoa(*((struct  in_addr*) &start_ip_n)))
				.arg(inet_ntoa(*((struct  in_addr*) &end_ip_n)));
			this->ip_range_box->addItem(str, QVariant::fromValue(dev));
		}
	}
	this->ui.mainToolBar->addWidget(this->ip_range_box);

	QAction *scan_action = new QAction(this);
	QIcon icon;
	icon.addFile(QString::fromUtf8(":/arptool/Resources/111.png"), QSize(), QIcon::Normal, QIcon::Off);
	scan_action->setIcon(icon);
	this->ui.mainToolBar->addAction(scan_action);

	connect(scan_action, SIGNAL(triggered(bool)), this, SLOT(onScan(bool)));
	//connect(&this->recv_thread, SIGNAL(findNewMachine(u_int32, u_int32)), this, SLOT(onFindNewMachine(u_int32, u_int32)));

	QStringList labels;
	labels<<"IP"<<"MAC"<<"HOST";
	this->ui.tableMachine->setColumnCount (labels.size());
	this->ui.tableMachine->setHorizontalHeaderLabels(labels);

	return true;
}

void ArpTool::onScan(bool)
{
	int index = this->ip_range_box->currentIndex ();
	EtherDevice dev = this->ip_range_box->itemData(index).value<EtherDevice>();

	pcap_t *adhandle = NULL;

	if (!recv_thread.isRunning() || !recv_thread.isDev(dev))
	{
		char errbuf[PCAP_ERRBUF_SIZE];
		/* open adapter */
		if ((adhandle = pcap_open_live(dev.name.c_str(), 65536, 1, 1000, errbuf))
			== NULL)
		{
			QString str = QString("Unable to open the adapter. %1 is not supported by WinPcap:%2")
				.arg(dev.name.c_str())
				.arg(errbuf);
			QMessageBox::warning(this, "tips", str);
			return ;
		}

		/* check if the ethernet*/
		if (pcap_datalink(adhandle) != DLT_EN10MB)	
		{
			QMessageBox::warning(this, "tips", "This program works only on Ethernet networks.");
			return ;
		}

		//only handle arp replies
		struct bpf_program fcode;
		int ret = pcap_compile(adhandle, &fcode, "arp and (ether[20:2]=0x2)", 1, dev.netmask);
		if (ret < 0)
		{
			QMessageBox::warning(this, "tips", "Compile filter failed");
			return ;
		}
		if (pcap_setfilter(adhandle, &fcode) < 0)
		{
			QMessageBox::warning(this, "tips", "Set filter failed");
			return ;
		}

		recv_thread.stop();
		recv_thread.setParam(adhandle, dev, this);
		recv_thread.start();
	}
	else
	{
		adhandle = this->recv_thread.getPcapHandle();
	}

	this->ui.tableMachine->setRowCount (0);

	//send arp request
	ToolKit toolkit;
	u_int32 host_order_netnask = ntohl(dev.netmask);
	u_int32 local_host_count = host_order_netnask ^ 0xffffffff;
	u_int32 host_order_local_ip = ntohl(dev.ip);
	u_int32 start_ip = host_order_netnask & host_order_local_ip;
	//toolkit.sendArp(adhandle, dev, start_ip + 11);
	for (u_int32 i = 1; i < local_host_count; i++)
	{
		toolkit.sendArp(adhandle, dev, start_ip + i);
	}
}

void ArpTool::onFindNewMachine(u_int32 ip, const u_int8* mac)
{
	ToolKit toolkit;
	QString ip_str = inet_ntoa(*((struct  in_addr*) &ip));
	QString mac_str = toolkit.toMacString(mac).c_str();
	int row = this->ui.tableMachine->rowCount();
	this->ui.tableMachine->insertRow(row);
	this->ui.tableMachine->setItem ( row, 0, new QTableWidgetItem(ip_str)) ;
	this->ui.tableMachine->setItem ( row, 1, new QTableWidgetItem(mac_str)) ;
//	this->ui.tableMachine->setItem ( row, 2, new QTableWidgetItem(toolkit.getHostName(ip).c_str())) ;
}
